﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint;
using Slam.Events;
using Slam.SharePoint;
using Slam.Configuration;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Text;

namespace Slam.FieldTypes
{
	/// <summary>
	/// SlamIdField rendering template control.
	/// </summary>
	public partial class SlamIdFieldControl : BaseFieldControl
	{
		protected Literal ltSlamId;
		protected TextBox txtSlamId;

		/// <summary>
		/// Indicates whether this field should automatically generate a SLAM ID
		/// </summary>
		public bool GenerateValue
		{
			get;
			set;
		}

		/// <summary>
		/// Indicates whether the interface for assigning the value of the Field is visible
		/// </summary>
		public bool ShowInForms
		{
			get;
			set;
		}

		/// <summary>
		/// Returns assigned value of the Field
		/// </summary>
		public override object Value
		{
			get
			{
				this.EnsureChildControls();
				return txtSlamId.Text;
			}
		}

		protected override string DefaultTemplateName
		{
			get
			{
				return "SLAMIDFieldControl";
			}
		}

		public override string DisplayTemplateName
		{
			get
			{
				return "SLAMIDFieldControl";
			}
		}

		/// <summary>
		/// Performs Item New/Edit Form validation relevant to SLAM.
		/// 
		/// If the configured type associated with the current Item has required associations,
		/// verify that the lookup fields corresponding to those associations have values.
		/// </summary>
		public override void Validate()
		{
			base.Validate();
			if (((base.ControlMode != SPControlMode.Display) && base.IsValid))
			{
				if (SlamItemContext.ListItem != null)
					SlamItemContext.ResetContext();

				IConfigurationManager slamConfiguration = ComponentService.GetInstance<IConfigurationManager>();
				DataMapping currentDataMapping = slamConfiguration.DataMapping;
				string siteCollection = SPControl.GetContextSite(HttpContext.Current).ServerRelativeUrl.TrimStart('/');
				if (!String.IsNullOrEmpty(siteCollection) && slamConfiguration.DataMappings.ContainsKey(siteCollection))
				{
					currentDataMapping = slamConfiguration.DataMappings[siteCollection];
				}

				ConfiguredType type = null;
				SPSecurity.RunWithElevatedPrivileges(delegate()
				{
					//GetSlamConfiguredType traverses entire site collection, use elevated privileges
					//to perform this operation
					using (SPSite elevatedSite = new SPSite(Web.Site.ID))
					{
						using (SPWeb elevatedWeb = elevatedSite.AllWebs[Web.ID])
						{
							SPList elevatedList = elevatedWeb.Lists[ListId];
							SPListItem elevatedListItem = elevatedList.Items.Add();

							elevatedListItem["ContentTypeId"] = Item["ContentTypeId"];
							if (Page.Request.QueryString["ContentTypeId"] != null && Page.Request.QueryString["ContentTypeId"] != Convert.ToString(elevatedListItem["ContentTypeId"]))
							{
								elevatedListItem["ContentTypeId"] = Page.Request.QueryString["ContentTypeId"];
							}
							type = elevatedListItem.GetSlamConfiguredType();
						}
					}
				});

				if (type != null)
				{
					//For every association field configured as Required in SLAM, verify the corresponding field has a selected value
					foreach (AssociationField field in currentDataMapping.GetTypeAssociations(type.Id, type.Name).Where(a => a.Required == true))
					{
						foreach (BaseFieldControl control in SPContext.Current.FormContext.FieldControlCollection)
						{
							if (control.Field != null && ((control is LookupField && control.Value == null) || (control is MultipleLookupField && (control.Value == null || ((SPFieldLookupValueCollection)control.Value).Count == 0))) && (control.Field.Title == field.Name || control.Field.InternalName == field.Name))
							{
								control.ErrorMessage = "This field is configured in SLAM as required and is missing a value.";
								control.IsValid = false;
								base.IsValid = false;
							}
						}
					}
				}
			}
		}

		protected override void CreateChildControls()
		{
			if (Field != null)
			{
				base.CreateChildControls();

				ltSlamId = (Literal)TemplateContainer.FindControl("ltSlamId");
				txtSlamId = (TextBox)TemplateContainer.FindControl("txtSlamId");

				if (ltSlamId != null && txtSlamId != null)
				{
					txtSlamId.Visible = false;

					if (GenerateValue && (ControlMode == SPControlMode.New || ItemFieldValue == null)) //Auto-Generate SLAM ID
					{
						object value = "";
						if (!Page.IsPostBack)
						{
							if (Page.Request.QueryString["SlamId"] != null)
							{
								try
								{
									value = Encoding.UTF8.GetString(Convert.FromBase64String(Page.Request.QueryString["SlamId"]));
								}
								catch { }
							}

							if (String.IsNullOrEmpty(value.ToString()))
							{
								//GetUniqueId(true, true) traverses entire site collection, use elevated privileges
								//to perform this operation
								try
								{
									SPSecurity.RunWithElevatedPrivileges(delegate()
									{
										using (SPSite elevatedSite = new SPSite(Web.Site.ID))
										{
											using (SPWeb elevatedWeb = elevatedSite.AllWebs[Web.ID])
											{
												SPList elevatedList = elevatedWeb.Lists[ListId];
												SPListItem elevatedListItem = null;
												if (ControlMode != SPControlMode.New)
												{
													try
													{
														elevatedListItem = elevatedList.Items.GetItemById(ItemId);
													}
													catch { }
												}
												if (elevatedListItem == null)
												{
													elevatedListItem = elevatedList.Items.Add();
												}
												elevatedListItem["ContentTypeId"] = Item["ContentTypeId"];
												if (Page.Request.QueryString["ContentTypeId"] != Convert.ToString(elevatedListItem["ContentTypeId"]))
												{
													elevatedListItem["ContentTypeId"] = Page.Request.QueryString["ContentTypeId"];
												}
												value = elevatedListItem.GetUniqueId(true, true);
											}
										}
									});
								}
								catch { }
							}
							ViewState["FieldValue"] = value;
						}
						else
						{
							value = (object)ViewState["FieldValue"];
						}
						ltSlamId.Text = value.ToString();
					}
					else if (!GenerateValue && (ControlMode == SPControlMode.New || ItemFieldValue == null)) //Manually assign SLAM ID
					{
						ltSlamId.Visible = false;
						txtSlamId.Visible = true;
					}
					else if (ItemFieldValue != null) //SLAM ID is assigned
					{
						ltSlamId.Text = Convert.ToString(ItemFieldValue);
					}
				}

				if (!Page.IsPostBack || GenerateValue)
				{
					txtSlamId.Text = ltSlamId.Text;
				}
			}
		}

		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);
			SPSecurity.RunWithElevatedPrivileges(delegate()
			{
				using (SPSite elevatedSite = new SPSite(Web.Site.ID))
				{
					using (SPWeb elevatedWeb = elevatedSite.AllWebs[Web.ID])
					{
						SPList elevatedList = elevatedWeb.Lists[ListId];

						if (!elevatedList.Fields.ContainsField("SLAM Properties"))
						{
							elevatedList.AddSlamPropertiesField();
							Page.Response.Redirect(Page.Request.Url.ToString());
						}
					}
				}
			});

			if (Page != null)
			{
				Page.Items[typeof(SlamIdFieldControl)] = this;
			}
		}

		protected override void OnLoad(EventArgs e)
		{
			SlamContext.Get(List).Actions.Register(SlamEvent.PreSave, listItem =>
			{
				if (ControlMode == SPControlMode.Edit && listItem != null && listItem.ContentType != null)
				{
					listItem.Web.AllowUnsafeUpdates = true;
					listItem.SetSlamProperty("PreviousContentTypeId", listItem.ContentTypeId.ToString());
				}
			});
			//Make sure field "container" is visible, do BaseFieldControl.OnLoad, 
			//then determine whether this rendering template control should be rendered
			this.Parent.Parent.Visible = true;
			base.OnLoad(e);
			this.Parent.Parent.Visible = ShowInForms;
		}

		public static SlamIdFieldControl GetCurrent(Page page)
		{
			SlamIdFieldControl idControl = null;
			if (page.Items[typeof(SlamIdFieldControl)] != null)
			{
				idControl = (SlamIdFieldControl)page.Items[typeof(SlamIdFieldControl)];
			}
			else
			{
				SlamIdFieldControl fieldControl = FindSlamIdFieldControl(page.Form);
				if (fieldControl != null)
				{
					idControl = fieldControl;
				}
			}
			return idControl;
		}

		private static SlamIdFieldControl FindSlamIdFieldControl(Control control)
		{
			SlamIdFieldControl fieldControl = null;
			foreach (Control childControl in control.Controls)
			{
				if (childControl is SlamIdFieldControl)
				{
					fieldControl = childControl as SlamIdFieldControl;
					break;
				}
				else if (childControl.Controls.Count > 0)
				{
					fieldControl = FindSlamIdFieldControl(childControl);
					if (fieldControl != null)
					{
						break;
					}
				}
			}
			return fieldControl;
		}
	}
}